#ifndef DISK_FILE_H_
#define DISK_FILE_H_

#include "base/macro_def.h"
#include <sys/uio.h>
#include <sys/mman.h>

class DiskFile {
  public:
    DiskFile(const std::string& path, int mode = O_CREAT | O_CLOEXEC | O_RDWR)
        : data_(NULL) {
      fd_ = ::open(path.c_str(), mode, 0644);
      CHECK_NE(fd_, -1)<< path;
    }

    explicit DiskFile(int fd)
    : fd_(fd), data_(NULL) {
      CHECK_NE(fd, -1) << fd;
    }

    static DiskFile* CreateDiskFile(int fd) {
      if (fd < 0) return NULL;
      return new DiskFile(fd);
    }

    virtual ~DiskFile() {
      UnMap();

      if (fd_ != -1) {
        int32 tmp = fd_;
        fd_ = -1;
        ::close(tmp);
      }
    }

    void Sync() const {
      int ret = fsync(fd_);
      PCHECK(ret == 0) << "fsync error";
    }

    void Truncate(int32 length) const {
      if (length < 0) return;
      int ret = ::ftruncate(fd_, length);
      PCHECK(ret != -1) << "ftruncate error";
    }

    char* Mmap(int prot = PROT_WRITE | PROT_READ, int flag=MAP_SHARED);

    const char* ReadOnlyMmap() {
      return Mmap(PROT_READ, MAP_PRIVATE);
    }

    void UnMap() {
      if (data_ != NULL) {
        ::munmap(data_, size());
        data_ = NULL;
      }
    }

    ssize_t size() const {
      struct stat st = {0};
      int ret = ::fstat(fd_, &st);
      if (ret == -1) {
        LOG(WARNING) << "fstat error, fd: " << fd_;
        return -1;
      }

      return st.st_size;
    }

    bool empty() const {
      return size() == 0;
    }

    bool Seek(int32 offset, int whence) const {
      int ret = ::lseek(fd_, offset, whence);
      if (ret == -1) {
        LOG(WARNING) << "lseek error, fd: " << fd_;
        return false;
      }
      return true;
    }

    int32 Position() const {
      int32 ret = ::lseek(fd_, 0, SEEK_CUR);
      if (ret == -1) {
        LOG(WARNING) << "lseek error, fd: " << fd_;
        return -1;
      }

      return ret;
    }

    int32 Read(char* buf, int32 size) const;
    int32 Read(char* buf, int32 size, int32 offset) const;
    int32 Read(struct iovec *iov, int32 iovcnt) const;

    int32 Write(const char* buf, int32 size) const;
    int32 Write(const char* buf, int32 size, int32 offset) const;
    void Write(const struct iovec* iov, int32 iovcnt) const;

    template<typename Type>
    int32 Read(Type* type) const {
      return Read((char*) type, sizeof(Type));
    }

    template<typename Type>
    int32 Write(const Type* type) const {
      return Write((const char*) type, sizeof(Type));
    }

    void CopyFrom(const DiskFile& rhs) const;

    private:
    int fd_;
    char* data_;

    DISALLOW_COPY_AND_ASSIGN(DiskFile);
  };

class ReadOnlyFile : public DiskFile {
  public:
    explicit ReadOnlyFile(const std::string& path)
        : DiskFile(path, O_RDONLY) {
    }

  private:
    DISALLOW_COPY_AND_ASSIGN(ReadOnlyFile);
};

class AppendOnlyFile : public DiskFile {
  public:
    explicit AppendOnlyFile(const std::string& path)
        : DiskFile(path, O_WRONLY | O_CREAT) {
    }

  private:
    DISALLOW_COPY_AND_ASSIGN(AppendOnlyFile);
};

#endif  // DISK_FILE_H_
